﻿using System;
using System.Runtime.Serialization;

namespace Sencha.Serialization.Serializers
{
    using Contracts;

    public sealed class CustomSerializer<ItemT> : TypeSerializer<ItemT>
    {
        public static readonly CustomSerializer<ItemT> Instance = new CustomSerializer<ItemT>();

        public override ItemT Deserialize(IJsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");
            System.Diagnostics.Contracts.Contract.EndContractBlock();

            // Null value
            if (reader.Token == JsonToken.Null)
            {
                if (typeof(ItemT).IsValueType && !(typeof(ItemT).IsGenericType && typeof(ItemT).GetGenericTypeDefinition() == typeof(Nullable<>)))
                    throw new Exceptions.UnexpectedToken(reader, JsonToken.BeginObject, JsonToken.BeginArray, JsonToken.Boolean, JsonToken.DateTime, JsonToken.Number, JsonToken.StringLiteral);

                return default(ItemT);
            }

            object instance = Activator.CreateInstance(typeof(ItemT));

            if (!(instance is IJsonSerializable))
                throw new Exceptions.TypeContractViolation(this.GetType(), this.TypeDescription, "implement IJsonSerializable");


            reader.Context.Hierarchy.Push(instance);

            (instance as IJsonSerializable).ReadJson(reader);

            reader.Context.Hierarchy.Pop();
            return (ItemT)instance;
        }
        public override void Serialize(IJsonWriter writer, ItemT value)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");
            System.Diagnostics.Contracts.Contract.EndContractBlock();

            if (value == null)
            {
                writer.WriteNull();
                return;
            }

            if (!(value is IJsonSerializable))
                throw new Exceptions.TypeContractViolation(this.GetType(), value.GetType(), "implement IJsonSerializable");

            writer.Context.Hierarchy.Push(value);
            (value as IJsonSerializable).WriteJson(writer);

            writer.Context.Hierarchy.Pop();
        }

        public override string ToString()
        {
            return string.Format("custom, {0}", this.TypeDescription);
        }
    }
}
